/*
 * Copyright (C) 2000-2005 Philips Electronics N.V.
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or 
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; see the file gnu_license.txt.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  
 * 
 */
#include "type.h"
#include "spectype.h"
#include "platform.h"

#ifndef MAX
#define MAX(a,b) ((a) > (b) ? (a) : (b))
#endif

/* For integral types:
     is_unsigned == !is_signed
*/

/* ----------------------------------------------------------------------------
 * Get the SpecType of T, make one if necessary
 */
Type
get_SpecType (Type t)
{
  SrcInfo si = no_srcinfo;
  size_t size = 0, align = 0;
  
  if (is_spectype(t)) return t;
  if (t) {
    si = Type_src_info (t);
    size = Type_size(t);
    align = Type_align(t);
  }
  return Create_SpecType (si, size, align, t,
                          FALSE, FALSE, FALSE, FALSE, FALSE, 0, FALSE, 0,
                          NULL);
}

/* ----------------------------------------------------------------------------
 * TRUE if the specs of A and B are compatible
 */
Bool
compatible_specs (Type a, Type b)
{
  a = get_SpecType (type_spec(a));
  b = get_SpecType (type_spec(b));

  /* Pre: the base types are compatible */
  if (user_compatible_attributes &&
      !user_compatible_attributes (type_attributes (a), type_attributes (b))) {
    return FALSE;
  }
  
  /* For two qualified types to be compatible, both shall have the
     identically qualified version of a compatible type. (3.5.3) */
  if (SpecType_is_restrict (a)  != SpecType_is_restrict (b))  return FALSE;
  if (SpecType_is_const (a)     != SpecType_is_const (b))     return FALSE;
  if (SpecType_is_volatile (a)  != SpecType_is_volatile (b))  return FALSE;

  /* bitfields shall have the same width (3.1.2.6) */
  if (SpecType_is_bit_field (a) != SpecType_is_bit_field (b)) return FALSE;
  else if (SpecType_is_bit_field (a)) {
    return SpecType_bit_width (a) == SpecType_bit_width (b);
  } 
  
#warning TODO: are int* and unsigned* assignment compatible?
#if 0
  if (SpecType_is_unsigned (a)  != SpecType_is_unsigned (b))  return FALSE;
#endif
  if (SpecType_is_long (a)      != SpecType_is_long (b))      return FALSE;

  return TRUE;
}

/* ----------------------------------------------------------------------------
 * TRUE if A has no specs B has
 */
Bool
disjoint_specs (Type a, Type b)
{
  if (!is_spectype(a) || !is_spectype(b)) return TRUE;
  if (SpecType_is_unsigned (a)  && SpecType_is_unsigned (b))  return FALSE;
  if (SpecType_is_restrict (a)  && SpecType_is_restrict (b))  return FALSE;
  if (SpecType_is_const (a)     && SpecType_is_const (b))     return FALSE;
  if (SpecType_is_volatile (a)  && SpecType_is_volatile (b))  return FALSE;
  if (SpecType_is_long (a)      && SpecType_is_long (b))      return FALSE;
  if (SpecType_is_bit_field (a) && SpecType_is_bit_field (b)) return FALSE;
  /* TODO: compare attributes */
  return TRUE;
}

/* ----------------------------------------------------------------------------
 * Remove SpecType of A if it does not add information
 */
static Type
smallest (Type a)
{
  if (!is_spectype(a)) return a;
  
  if (SpecType_is_unsigned (a))   return a;
  if (SpecType_is_restrict (a))   return a;
  if (SpecType_is_const (a))      return a;
  if (SpecType_is_volatile (a))   return a;
  if (SpecType_is_long (a))       return a;
  if (SpecType_is_bit_field (a))  return a;
  if (SpecType_attr (a))          return a;

  return SpecType_base (a);
}


/* ----------------------------------------------------------------------------
 * Combine the attribute A and B into one
 */
gnu_attr
both_attributes (gnu_attr a, gnu_attr b)
{
  gnu_attr r = NULL;

  if (!a && !b) return NULL;
  if (!a) return both_attributes(b,a);

  r = COPY_gnu_attr (a);
  if (b) {
    gnu_attr_attrs (r) = FRONT_CONCAT (attribute,
                                       gnu_attr_attrs (b),
                                       gnu_attr_attrs (r));
  }
  if (user_parse_type_attribute) {
    r = user_parse_type_attribute (r);
  }
  return r;
}

/* ----------------------------------------------------------------------------
 * The combined, baseless,  SpecType of A and B
 * Note: removes bitfield spec
 */
Type
both_specs (Type a, Type b)
{
  Type r = a;

  if (!is_spectype(a) && !is_spectype(b)) return NULL;
  if (!is_spectype(a))      r = copy_type (b);
  else if (!is_spectype(b)) r = copy_type (a);
  else {
    /* both are SpecType */
    r = Create_SpecType (Type_src_info (a),
                         0, 0, NULL,
                         SpecType_is_signed(a)    || SpecType_is_signed(b),
                         SpecType_is_unsigned(a)  || SpecType_is_unsigned(b),
                         SpecType_is_restrict(a)  || SpecType_is_restrict(b),
                         SpecType_is_const(a)     || SpecType_is_const(b),
                         SpecType_is_volatile(a)  || SpecType_is_volatile(b),
                         MAX(SpecType_is_long(a),  SpecType_is_long(b)),
                         FALSE, 0,
                         both_attributes (SpecType_attr (a),
                                          SpecType_attr (b)) );
    assert (!SpecType_is_signed (r) || !SpecType_is_unsigned (r));
  }
  SpecType_base(r) = NULL;
  return r;  
}

/* ----------------------------------------------------------------------------
 * Merge the specs of B into A,
 * Note: should only be used during typing
 */
Type
combine_specs (SpecType a, SpecType b)
{
  SpecType_is_long(a) += SpecType_is_long(b);
  SpecType_is_signed(a)   = SpecType_is_signed(a) || SpecType_is_signed(b);
  SpecType_is_unsigned(a) = SpecType_is_unsigned(a) || SpecType_is_unsigned(b);
  SpecType_is_restrict(a) = SpecType_is_restrict(a) || SpecType_is_restrict(b);
  SpecType_is_const(a)    = SpecType_is_const(a)    || SpecType_is_const(b);
  SpecType_is_volatile(a) = SpecType_is_volatile(a) || SpecType_is_volatile(b);
  if (SpecType_is_bit_field(b)) {
    if (SpecType_is_bit_field(a)) return NULL;
    SpecType_is_bit_field(a) = TRUE;
    SpecType_bit_width(a) = SpecType_bit_width(b);
  }
  SpecType_attr(a) = both_attributes (SpecType_attr(a), SpecType_attr(b));
  return a;
}

/* ----------------------------------------------------------------------------
 * A with SpecType from B
 */
Type
copy_specs (Type a, Type b)
{
  Type r;
  
  if (!is_spectype(b)) return base_type(a);
  r = copy_type (b);
  SpecType_base(r) = base_type(a);
  Type_size(r)  = Type_size(a);
  Type_align(r) = Type_align(a);
  return r;
}

/* ----------------------------------------------------------------------------
 * Get the complete type specifier of T
 */
Type
type_spec (Type t)
{
  Type r = NULL;
  Type u = expanded_type (t);
  while (is_spectype (u)) {
    r = both_specs (u, r);
    u = expanded_type (SpecType_base(u));
  }
  return r;
}

/* ----------------------------------------------------------------------------
 * Remove SpecType from TYPE
 */
Type
base_type (Type type)
{
  if (is_spectype(type)) return SpecType_base(type);
  return type;
}

/* T -> signed T */
Type
make_signed(Type t)
{
  Type u = get_SpecType (t);
  SpecType_is_unsigned (u) = FALSE;
  SpecType_is_signed (u) = TRUE;
  return u;
}

/* T -> unsigned T */
Type
make_unsigned(Type t)
{
  Type u = get_SpecType (t);
  SpecType_is_unsigned (u) = TRUE;
  SpecType_is_signed (u) = FALSE;
  return u;
}

/* T -> const T */
/* T [] -> (const T)[] */
Type
make_const(Type t)
{
  Type u;
  if (is_array_type (t)) {
    ArrayType_element (t) = make_const (ArrayType_element (t));
    return t;
  }
  u = get_SpecType (t);
  SpecType_is_const (u) = TRUE;
  return u;
}

/* T -> volatile T */
/* T [] -> (volatile T)[] */
Type
make_volatile(Type t)
{
  Type u = get_SpecType (t);
  if (is_array_type (t)) {
    ArrayType_element (t) = make_volatile (ArrayType_element (t));
    return t;
  }
  SpecType_is_volatile (u) = TRUE;
  return u;
}

/* T -> restrict T */
Type
make_restrict(Type t)
{
  Type u = get_SpecType (t);
  SpecType_is_restrict (u) = TRUE;
  return u;
}

/* T -> long T */
Type
make_long(Type t)
{
  Type u = get_SpecType (t);
  SpecType_is_long (u) = 1;
  return u;
}

/* T -> long long T */
Type
make_long_long(Type t)
{
  Type u = get_SpecType (t);
  SpecType_is_long (u) = 2;
  return u;
}

/* T -> T:width */
Type
make_bitfield(Type t, int width)
{
  Type u = get_SpecType (t);
  SpecType_is_bit_field (u) = TRUE;
  SpecType_bit_width (u) = width;
  return u;
}

/* signed T -> T */
Type
clear_signed(Type t)
{
  Type u;
  if (!is_spectype(t)) return t;
  u = get_SpecType (t);
  SpecType_is_signed (u) = FALSE;
  return smallest(u);
}

/* unsigned T -> T */
Type
clear_unsigned(Type t)
{
  Type u;
  if (!is_spectype(t)) return t;
  u = get_SpecType (t);
  SpecType_is_unsigned (u) = FALSE;
  return smallest(u);
}

/* const T -> T */
/* (const T) [] -> T [] */
Type
clear_const(Type t)
{
  Type u;
  if (is_array_type (t)) {
    ArrayType_element (t) = clear_const (ArrayType_element (t));
    return t;
  }
  if (!is_spectype(t)) return t;
  u = get_SpecType (t);
  SpecType_is_const (u) = FALSE;
  return smallest(u);
}

/* volatile T -> T */
/* (volatile T) [] -> T [] */
Type
clear_volatile(Type t)
{
  Type u;
  if (is_array_type (t)) {
    ArrayType_element (t) = clear_volatile (ArrayType_element (t));
    return t;
  }
  if (!is_spectype(t)) return t;
  u = get_SpecType (t);
  SpecType_is_volatile (u) = FALSE;
  return smallest(u);
}

/* restrict T -> T */
Type
clear_restrict(Type t)
{
  Type u;
  if (!is_spectype(t)) return t;
  u = get_SpecType (t);
  SpecType_is_restrict (u) = FALSE;
  return smallest(u);
}

/* long T -> T */
Type
clear_long(Type t)
{
  Type u;
  if (!is_spectype(t)) return t;
  u = get_SpecType (t);
  SpecType_is_long (u) = 0;
  return smallest(u);
}

/* long long T -> T */
Type
clear_long_long(Type t)
{
  Type u;
  if (!is_spectype(t)) return t;
  u = get_SpecType (t);
  SpecType_is_long (u) = 0;
  return smallest(u);
}

/* T:width -> T */
Type
clear_bitfield(Type t)
{
  Type u;
  if (!is_spectype(t)) return t;
  u = get_SpecType (t);
  SpecType_is_bit_field (u) = FALSE;
  return smallest(u);
}

/* T __attribute__ ((X)) -> T  __attribute__ ((X, attrs)) */
/* (T __attribute__ ((X))) [] -> (T  __attribute__ ((X, attrs))) [] */
Type
type_add_attributes(Type t, gnu_attr attrs)
{
  Type u;
  if (!attrs) return t;
  if (is_array_type (t)) {
    ArrayType_element (t) = type_add_attributes (ArrayType_element (t), attrs);
    return t;
  }
  u = get_SpecType (t);
  SpecType_attr(u) = both_attributes (SpecType_attr(u), attrs);
  return u;
}

/* T  __attribute__ ((X)) -> T */
/* (T  __attribute__ ((X))) [] -> T [] */
Type
type_remove_attributes(Type t)
{
  gnu_attr attrs;
  Type u;

  if (is_array_type (t)) {
    ArrayType_element (t) = type_remove_attributes (ArrayType_element (t));
    return t;
  }
  attrs = type_attributes (t);
  if (!attrs) return t;
  u = get_SpecType (t);
  SpecType_attr (u) = NULL;
  return smallest(u);
}

gnu_attr
type_attributes (Type t)
{
  if (is_array_type (t)) {
    return type_attributes (ArrayType_element (t));
  }
  return type_attributes_shallow (t);
}

gnu_attr
type_attributes_shallow (Type t)
{
  if (!is_spectype(t)) return NULL;
  return SpecType_attr (t);
}
